home *** CD-ROM | disk | FTP | other *** search
/ Programming Languages Suite / ProgramD2.iso / Borland / Borland C++ V5.02 / OWLSRC.PAK / GLYPHBTN.CPP < prev    next >
Encoding:
C/C++ Source or Header  |  1997-05-06  |  23.9 KB  |  998 lines

  1. //----------------------------------------------------------------------------
  2. // ObjectWindows
  3. // Copyright (c) 1995, 1997 by Borland International, All Rights Reserved
  4. //
  5. //$Revision:   10.23  $
  6. //
  7. // Implementation of TGlyphButton
  8. //----------------------------------------------------------------------------
  9. #include <owl/pch.h>
  10. #if !defined(OWL_GLYPHBTN_H)
  11. # include <owl/glyphbtn.h>
  12. #endif
  13. #if !defined(OWL_UIHELPER_H)
  14. # include <owl/uihelper.h>
  15. #endif
  16. #if !defined(OWL_GDIOBJEC_H)
  17. # include <owl/gdiobjec.h>
  18. #endif
  19. #if !defined(WINSYS_COLOR_H)
  20. # include <winsys/color.h>
  21. #endif
  22. #if !defined(WINSYS_SYSTEM_H)
  23. # include <winsys/system.h>
  24. #endif
  25.  
  26. const int  LayoutMargin         = 4;
  27. const int  FaceToFocusRectDelta = -1;
  28. const int  BUTTONSTATE_PUSHED   = 0x0004;
  29. const int  BUTTONSTATE_FOCUS    = 0x0008;
  30. const int  BUTTONSTYLE_MASK     = 0x00FF;
  31. const long RopDSPDxax           = 0x00E20746L;
  32.  
  33. OWL_DIAGINFO;
  34. DIAG_DECLARE_GROUP(OwlControl);        // General Controls diagnostic group
  35.  
  36. // Hard-coded color constant that a bitmap designer can use as background.
  37. // This color RGB(192, 192, 192) can then be mapped to the BTNFACE color
  38. // at runtime.
  39. //
  40. TColor TBtnBitmap::DefaultFaceColor = TColor::LtGray;
  41.  
  42. //
  43. // Constructor of BtnBitmap - Loads the specified bitmap and updates the
  44. // face color if necessary.
  45. //
  46. TBtnBitmap::TBtnBitmap(HINSTANCE hInstance, TResId resId,
  47.                        const TColor& faceColor)
  48. :
  49.   TBitmap(hInstance, resId),
  50.   FaceColor(faceColor)
  51. {
  52.   UpdateFaceColor();
  53. }
  54.  
  55. //
  56. // Constructor of BtnBitmap - aliases the specified bitmap handle and
  57. // updates the face color if necessary
  58. //
  59. TBtnBitmap::TBtnBitmap(HBITMAP hBitmap, const TColor& faceColor,
  60.                        TAutoDelete autoDelete)
  61. :
  62.   TBitmap(hBitmap, autoDelete),
  63.   FaceColor(faceColor)
  64. {
  65.   UpdateFaceColor();
  66. }
  67.  
  68. //
  69. // Updates the face color of the associated bitmap if the current face
  70. // color does not match the 3DFACE system color.
  71. //
  72. void
  73. TBtnBitmap::UpdateFaceColor()
  74. {
  75.   if (FaceColor != TColor::Sys3dFace) {
  76.     TBitmap copy(*this);
  77.     TMemoryDC dstDC, srcDC;
  78.     dstDC.SelectObject(*this);
  79.     srcDC.SelectObject(copy);
  80.     MapColor(dstDC, srcDC, TColor::Sys3dFace, FaceColor, Width(), Height());
  81.     dstDC.RestoreBitmap();
  82.     srcDC.RestoreBitmap();
  83.     FaceColor = TColor::Sys3dFace;
  84.   }
  85. }
  86.  
  87. //
  88. // Helper routine used to map the face color of the underlying bitmap
  89. //
  90. void
  91. TBtnBitmap::MapColor(TDC& dc, TDC& srcDC, const TColor& toColor,
  92.                      const TColor& fromColor, int width, int height)
  93. {
  94.   // Create a monochrome mask
  95.   //
  96.   TMemoryDC hMemDC(srcDC);
  97.   TBitmap hMask(width, height);
  98.   hMemDC.SelectObject(hMask);
  99.  
  100.   // Build a mask where the source color is zeroed
  101.   //
  102.   TColor crBack = srcDC.SetBkColor(fromColor);
  103.   hMemDC.BitBlt(0, 0, width, height, srcDC, 0, 0, SRCCOPY);
  104.   srcDC.SetBkColor(crBack);
  105.  
  106.   // Make a brush out of the destination color
  107.   //
  108.   TBrush brush(toColor);
  109.   dc.SelectObject(brush);
  110.  
  111.   // Copy the mask to the destination
  112.   //
  113.   dc.BitBlt(0, 0, width, height, srcDC, 0, 0, SRCCOPY);
  114.  
  115.   // Copy the source bitmap to the destination by applying the
  116.   // brush where pixels are zeroed
  117.   //
  118.   dc.BitBlt(0, 0, width, height, hMemDC, 0, 0, RopDSPDxax);
  119.   dc.RestoreBrush();
  120. }
  121.  
  122. //
  123. // Constructor of TGlyphButton - Use this constructor to create a GlyphBtn
  124. // from scratch.
  125. //
  126. TGlyphButton::TGlyphButton(TWindow* parent, int id, const char far* text,
  127.                            int X, int Y, int W, int H, bool isDefault,
  128.                            TModule* module)
  129. :
  130.   TButton(parent, id, text, X, Y, W, H, isDefault, module)
  131. {
  132.   InitVars();
  133. }
  134.  
  135. //
  136. // Constructor of TGlyphButton - Use this constructor to alias a glyph button
  137. // control specified in a dialog template.
  138. //
  139. TGlyphButton::TGlyphButton(TWindow* parent, int resourceId, TModule* module)
  140. :
  141.   TButton(parent, resourceId, module)
  142. {
  143.   InitVars();
  144. }
  145.  
  146. //
  147. // Method used to initialized variables used by GlyphButton's implementation
  148. //
  149. void
  150. TGlyphButton::InitVars()
  151. {
  152.   UpBmp = 0;
  153.   DownBmp = 0;
  154.   FocusBmp = 0;
  155.   DisabledBmp = 0;
  156.   BtnFont = new TDefaultGUIFont;
  157.   xText = yText = -1;
  158.   xGlyph = yGlyph = -1;
  159.   LayStyle = lsH_GST;
  160.   Set(biShowText);
  161. }
  162.  
  163. //
  164. // Destructor - Cleanup resources used by Glyph Button object
  165. //
  166. TGlyphButton::~TGlyphButton()
  167. {
  168.   delete UpBmp;
  169.   delete DownBmp;
  170.   delete FocusBmp;
  171.   delete DisabledBmp;
  172.   delete BtnFont;
  173. }
  174.  
  175. //
  176. // Response Table
  177. //
  178. DEFINE_RESPONSE_TABLE1(TGlyphButton, TButton)
  179.   EV_WM_PAINT,
  180.   EV_WM_ERASEBKGND,
  181.   EV_WM_SETFOCUS,
  182.   EV_WM_KILLFOCUS,
  183.   EV_WM_GETFONT,
  184.   EV_WM_SETFONT,
  185.   EV_WM_GETDLGCODE,
  186.   EV_WM_LBUTTONDOWN,
  187.   EV_WM_LBUTTONDBLCLK,
  188.   EV_WM_LBUTTONUP,
  189.   EV_WM_MOUSEMOVE,
  190.   EV_WM_KEYDOWN,
  191.   EV_WM_KEYUP,
  192.   EV_WM_ENABLE,
  193.   EV_WM_CANCELMODE,
  194.   EV_MESSAGE(BM_SETSTATE, BmSetState),
  195.   EV_MESSAGE(BM_GETSTATE, BmGetState),
  196.   EV_MESSAGE(BM_SETSTYLE, BmSetStyle),
  197. END_RESPONSE_TABLE;
  198.  
  199. //
  200. // Window proc. of control to handle messages sent before OWL thunks
  201. //
  202. LRESULT CALLBACK OWL_EXPORT16
  203. BButtonProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam)
  204. {
  205.   switch(msg) {
  206.     case WM_GETDLGCODE: {
  207.       uint32 style = GetWindowLong(hwnd, GWL_STYLE);
  208.       uint16 btnStyle = uint16(LoUint16(style) & BUTTONSTYLE_MASK);
  209.       if (btnStyle == BS_DEFPUSHBUTTON)
  210.         return DLGC_BUTTON|DLGC_DEFPUSHBUTTON;
  211.       else
  212.         return DLGC_BUTTON|DLGC_UNDEFPUSHBUTTON;
  213.     }
  214.   }
  215.   return DefWindowProc(hwnd, msg, wParam, lParam);
  216. }
  217.  
  218. //
  219. // Overriden virtual of TWindow - Fills out information about the Window
  220. // class associated with a glyph button.
  221. // NOTE: The class information is based on the system's "BUTTON" class.
  222. //
  223. void
  224. TGlyphButton::GetWindowClass(WNDCLASS& wndClass)
  225. {
  226.   // Grab a the attributes of the native "BUTTON" control
  227.   //
  228.   if (::GetClassInfo(0, "BUTTON", &wndClass)) {
  229.     wndClass.hInstance = *GetModule();
  230.     wndClass.lpszClassName = GetClassName();
  231.     wndClass.lpfnWndProc = BButtonProc;
  232.   }
  233.   else {
  234.     TControl::GetWindowClass(wndClass);
  235.     wndClass.style = CS_HREDRAW|CS_VREDRAW|CS_PARENTDC;
  236.     wndClass.hbrBackground = HBRUSH(COLOR_BTNFACE+1);
  237.   }
  238. }
  239.  
  240. //
  241. // Return name of window class associated with a glyph button control.
  242. //
  243. char far*
  244. TGlyphButton::GetClassName()
  245. {
  246.   return OWL_GLYPHBTN;
  247. }
  248.  
  249. //
  250. // Overriden virtual of TWindow - Updates internal flags based on style of
  251. // underlying window.
  252. //
  253. void
  254. TGlyphButton::SetupWindow()
  255. {
  256.   TButton::SetupWindow();
  257.  
  258.   // Update state flags based on current style
  259.   //
  260.   uint32 style = GetStyle();
  261.   if (style & BS_DEFPUSHBUTTON)
  262.     Set(biDefault);
  263.   if (style & WS_DISABLED)
  264.     Set(biDisabled);
  265. }
  266.  
  267. //
  268. // Specifies a bitmap to be used as glyph
  269. //
  270. void
  271. TGlyphButton::SetGlyph(HBITMAP hBitmap, TGlyphType type, TAutoDelete autoDelete)
  272. {
  273. #if defined(BI_PLAT_WIN16)
  274.   PRECONDITION(!hBitmap || ::IsGDIObject(hBitmap) != 0);
  275. #else
  276.   PRECONDITION(!hBitmap || ::GetObjectType(hBitmap) == OBJ_BITMAP);
  277. #endif
  278.  
  279.   SetGlyph(hBitmap ? new TBitmap(hBitmap, autoDelete) : (TBitmap*)0, type);
  280. }
  281.  
  282. //
  283. // Specify the resource identifier of a bitmap to be used as glyph
  284. //
  285. void
  286. TGlyphButton::SetGlyph(TResId resId, TModule* module, TGlyphType type)
  287. {
  288.   PRECONDITION(module != 0 || GetModule());
  289.   SetGlyph(new TBitmap(module ? *module : *GetModule(), resId), type);
  290. }
  291.  
  292. //
  293. // Specify a bitmap object to be used as glyph
  294. // NOTE: The 'bitmap' parameter can be 0 to reset the glyph stored by the
  295. //       glyph button object.
  296. //
  297. void
  298. TGlyphButton::SetGlyph(TBitmap* bitmap, TGlyphType type)
  299. {
  300.   PRECONDITION(!bitmap || bitmap->IsGDIObject());
  301.  
  302.   switch (type) {
  303.     case gtUp:
  304.       delete UpBmp;
  305.       UpBmp = bitmap;
  306.       break;
  307.  
  308.     case gtDown:
  309.       delete DownBmp;
  310.       DownBmp = bitmap;
  311.       break;
  312.  
  313.     case gtFocus:
  314.       delete FocusBmp;
  315.       FocusBmp = bitmap;
  316.       break;
  317.  
  318.     case gtDisabled:
  319.       delete DisabledBmp;
  320.       DisabledBmp = bitmap;
  321.       break;
  322.  
  323.     default:
  324.       break;
  325.   }
  326.  
  327.   // Update status flag
  328.   //
  329.   if (UpBmp)
  330.     Set(biShowGlyph);
  331.   else
  332.     Clear(biShowGlyph);
  333. }
  334.  
  335. //
  336. // WM_PAINT handler - Invokes 'Paint' method to display glyph and/or text.
  337. //
  338. void
  339. TGlyphButton::EvPaint()
  340. {
  341.   TPaintDC dc(*this);
  342.   TRect&   rect = *(TRect*)&dc.Ps.rcPaint;
  343.   Paint(dc, dc.Ps.fErase, rect);
  344. }
  345.  
  346. //
  347. // Invoke 'PaintButton' to display glyph and/or text.
  348. //
  349. void
  350. TGlyphButton::Paint(TDC& dc, bool /*erase*/, TRect& /*rect*/)
  351. {
  352.   PaintButton(dc);
  353. }
  354.  
  355.  
  356. //
  357. // WM_ERASEBKGND handler - Return true to prevent background from being
  358. // erased. (WM_PAINT handler paints whole client area).
  359. //
  360. bool
  361. TGlyphButton::EvEraseBkgnd(HDC /*dc*/)
  362. {
  363.   return true;
  364. }
  365.  
  366. //
  367. // WM_SETFOCUS handler - Updates internal flag and forces button to redraw
  368. //
  369. void
  370. TGlyphButton::EvSetFocus(THandle /*hWndLostFocus*/)
  371. {
  372.   Set(biFocus);
  373.   Invalidate(true);
  374. }
  375.  
  376. //
  377. // WM_KILLFOCUS handler - Updates internal flag and forces button to redraw
  378. //
  379. void
  380. TGlyphButton::EvKillFocus(THandle /*hWndGetFocus*/)
  381. {
  382.   Clear(biFocus);
  383.   if (IsSet(biPushed))
  384.     ClearCapture();
  385.   else
  386.     Invalidate(true);
  387. }
  388.  
  389. //
  390. // WM_GETFONT handler - Returns font used by control if one was specified
  391. // earlier. Otherwise, returns 0.
  392. //
  393. HFONT
  394. TGlyphButton::EvGetFont()
  395. {
  396.   PRECONDITION(!BtnFont || BtnFont->IsGDIObject());
  397.   return BtnFont ? HFONT(*BtnFont) : HFONT(0);
  398. }
  399.  
  400. //
  401. // WM_SETFONT Handler. Deletes any cached font and stores copy of new one.
  402. //
  403. void
  404. TGlyphButton::EvSetFont(HFONT hFont, bool redraw)
  405. {
  406.   delete BtnFont;
  407.   BtnFont = new TFont(hFont);
  408.   if (redraw)
  409.     Invalidate();
  410. }
  411.  
  412. //
  413. // WM_GETDLGCODE handler. Inform dialog manager that we're a 'normal' push
  414. // button or the default push button according to our style.
  415. //
  416. uint
  417. TGlyphButton::EvGetDlgCode(MSG* /*msg*/)
  418. {
  419.   if (IsSet(biDefault))
  420.     return DLGC_BUTTON|DLGC_DEFPUSHBUTTON;
  421.   else
  422.     return DLGC_BUTTON|DLGC_UNDEFPUSHBUTTON;
  423. }
  424.  
  425. //
  426. // WM_LBUTTONDOWN handler. Grab focus and update button's state to be in
  427. // 'pushed' mode.
  428. //
  429. void
  430. TGlyphButton::EvLButtonDown(uint /*modKeys*/, TPoint& /*point*/)
  431. {
  432.   SetCapture();
  433.   SendMessage(BM_SETSTATE, TRUE);
  434.   if (!IsSet(biFocus))
  435.     SetFocus();
  436. }
  437.  
  438. //
  439. // WM_LBUTTONDBLCLK handler. Simply forward to LBUTTONDOWN handler
  440. //
  441. void
  442. TGlyphButton::EvLButtonDblClk(uint modKeys, TPoint& point)
  443. {
  444.   EvLButtonDown(modKeys, point);
  445. }
  446.  
  447. //
  448. // WM_LBUTTONUP handler. Restore state of button and notify parent with a
  449. // CLICKED message if necessary.
  450. //
  451. void
  452. TGlyphButton::EvLButtonUp(uint /*modKeys*/, TPoint& point)
  453. {
  454.   if (GetCapture() == *this) {
  455.     ReleaseCapture();
  456.  
  457.     SendMessage(BM_SETSTATE, FALSE);
  458.  
  459.     TRect rect;
  460.     GetClientRect(rect);
  461.  
  462.     if (rect.Contains(point)) {
  463.       SendNotification(::GetParent(*this), GetDlgCtrlID(),
  464.                        BN_CLICKED, *this);
  465.     }
  466.   }
  467. }
  468.  
  469. //
  470. // WM_MOUSEMOVE handler. Update state of button if we're in 'capture' mode.
  471. //
  472. void
  473. TGlyphButton::EvMouseMove(uint modKeys, TPoint& point)
  474. {
  475.   if (modKeys & MK_LBUTTON && GetCapture() == *this) {
  476.     TRect rect;
  477.     GetClientRect(rect);
  478.  
  479.     if (rect.Contains(point))
  480.       SendMessage(BM_SETSTATE, TRUE);
  481.     else
  482.       SendMessage(BM_SETSTATE, FALSE);
  483.   }
  484. }
  485.  
  486. //
  487. // WM_KEYDOWN handler. Update state of button upon detecting that user
  488. // pressed the space bar.
  489. //
  490. void
  491. TGlyphButton::EvKeyDown(uint key, uint /*repeatCount*/, uint /*flags*/)
  492. {
  493.   if (key == VK_SPACE)
  494.     SendMessage(BM_SETSTATE, TRUE);
  495. }
  496.  
  497. //
  498. // WM_KEYUP handler. Restore state of button and notify parent
  499. //
  500. void
  501. TGlyphButton::EvKeyUp(uint key, uint /*repeatCount*/, uint /*flags*/)
  502. {
  503.   if (IsSet(biPushed) && key == VK_SPACE) {
  504.     SendMessage(BM_SETSTATE, FALSE);
  505.  
  506.     SendNotification(::GetParent(*this), GetDlgCtrlID(),
  507.                       BN_CLICKED, *this);
  508.   }
  509. }
  510.  
  511. //
  512. // WM_ENABLE handler. Update internal flags and invalidate control if
  513. // necessary.
  514. //
  515. void
  516. TGlyphButton::EvEnable(bool enabled)
  517. {
  518.   if (enabled) {
  519.     Clear(biDisabled);
  520.   }
  521.   else {
  522.     ClearCapture();
  523.     Set(biDisabled);
  524.   }
  525.   Invalidate(true);
  526. }
  527.  
  528. //
  529. //
  530. //
  531. void
  532. TGlyphButton::EvCancelMode()
  533. {
  534.   ClearCapture();
  535. }
  536.  
  537. //
  538. // BM_GETSTATE handler. Return the current state of the window.
  539. //
  540. TResult
  541. TGlyphButton::BmGetState(TParam1 /*param1*/, TParam2 /*param2*/)
  542. {
  543.   TResult result = 0;
  544.   if (IsSet(biPushed))
  545.     result |= BUTTONSTATE_PUSHED;
  546.  
  547.   if (IsSet(biFocus))
  548.     result |= BUTTONSTATE_FOCUS;
  549.  
  550.   return result;
  551. }
  552.  
  553. //
  554. // BM_SETSTATE handler. Update internal state flags based on parameters and
  555. // redraw control if necessary.
  556. //
  557. TResult
  558. TGlyphButton::BmSetState(TParam1 param1, TParam2 /*param2*/)
  559. {
  560.   if (param1) {
  561.     // Needs hilight look
  562.     //
  563.     if (!IsSet(biPushed)) {
  564.       Set(biPushed);
  565.       PaintNow();
  566.     }
  567.   }
  568.   else {
  569.     // Needs normal look
  570.     //
  571.     if (IsSet(biPushed)) {
  572.       Clear(biPushed);
  573.       PaintNow();
  574.     }
  575.   }
  576.   return 0;
  577. }
  578.  
  579. //
  580. // BM_SETSTYLE handler. Update internal flags to match specified parameters
  581. // and invalidate the window if necessary.
  582. //
  583. TResult
  584. TGlyphButton::BmSetStyle(TParam1 param1, TParam2 /*param2*/)
  585. {
  586.   // Grab and splice the styles
  587.   //
  588.   uint32 style = GetStyle();
  589.   uint16 winStyle = HiUint16(style);
  590.   uint16 btnStyle = uint16(LoUint16(style) & BUTTONSTYLE_MASK);
  591.  
  592.   // Check against passed in parameter
  593.   // NOTE: We only cater to PUSHBUTTON and DEFPUSHBUTTON
  594.   //       The current definition so BS_PUSHBUTTON is 0L
  595.   //
  596.   if (LOWORD(param1) == BS_PUSHBUTTON  &&  btnStyle != BS_PUSHBUTTON) {
  597.  
  598.     // Make 'simple' push button
  599.     //
  600.     TWindow::SetStyle(MAKELONG(param1, winStyle));
  601.     Clear(biDefault);
  602.     Invalidate(true);
  603.   }
  604.   else if (LOWORD(param1)==BS_DEFPUSHBUTTON && btnStyle != BS_DEFPUSHBUTTON){
  605.  
  606.     // Make 'default' push button
  607.     //
  608.     TWindow::SetStyle(MAKELONG(param1, winStyle));
  609.     Set(biDefault);
  610.     Invalidate(true);
  611.   }
  612.  
  613. #if defined(__DEBUG) || defined(__TRACE) || defined(__WARN)
  614.   if (LOWORD(param1) != BS_PUSHBUTTON &&
  615.       LOWORD(param1) != BS_DEFPUSHBUTTON)
  616.     TRACEX(OwlControl, 0, "BmSetStyle: Invalid style specified");
  617. #endif
  618.  
  619.   return 0;
  620. }
  621.  
  622. //
  623. // Release caption if we are in 'capture' mode. Reset internal flags
  624. // appropriately.
  625. //
  626. void
  627. TGlyphButton::ClearCapture()
  628. {
  629.   if (GetCapture() == *this)
  630.     ReleaseCapture();
  631.   Clear(biPushed);
  632.   Invalidate(true);
  633. }
  634.  
  635. //
  636. // Paint the button into a memory DC and bitblt the final rendering to the
  637. // specified 'dc'.
  638. //
  639. void
  640. TGlyphButton::PaintButton(TDC& dc)
  641. {
  642.   TRect rect;
  643.   GetClientRect(rect);
  644.  
  645.   // Create compatible bitmap
  646.   //
  647.   TBitmap memBmp(dc, rect.Width(), rect.Height());
  648.  
  649.   // Create compatible memory DC
  650.   //
  651.   TMemoryDC memDC(dc);
  652.  
  653.   // Select and init memory bitmap
  654.   //
  655.   memDC.SelectObject(memBmp);
  656.  
  657.   // Save the rectangle for bitblt'ing - 'rect' will be adjusted
  658.   // as we move from defaultFrame, border and face...
  659.   //
  660.   TRect bltRect = rect;
  661.  
  662.   // Paint the button into the memory DC
  663.   //
  664.   PaintDefaultRect(memDC, rect);
  665.   PaintFrame(memDC, rect);
  666.   PaintFace(memDC, rect);
  667.  
  668.   // Bitblt the button to the output device
  669.   //
  670.   dc.BitBlt(bltRect, memDC, TPoint(bltRect.left, bltRect.top));
  671.  
  672.   // Cleanup
  673.   //
  674.   memDC.RestoreBitmap();
  675. }
  676.  
  677. //
  678. // Draw the border of the button
  679. //
  680. void
  681. TGlyphButton::PaintFrame(TDC& dc, TRect& rect)
  682. {
  683.   // Paint the border
  684.   //
  685.   TUIBorder uiBorder(rect, IsSet(biPushed) ? TUIBorder::ButtonDn :
  686.                                              TUIBorder::ButtonUp);
  687.   uiBorder.Paint(dc);
  688.  
  689.   // Shrink the rectangle to leave the face
  690.   //
  691.   rect = uiBorder.GetClientRect();
  692. }
  693.  
  694. //
  695. // Draw a frame around the button if it's a default push button.
  696. //
  697. void
  698. TGlyphButton::PaintDefaultRect(TDC& dc, TRect& rect)
  699. {
  700.   if (IsSet(biDefault)) {
  701.     if (rect.Width() > 2 && rect.Height() > 2) {
  702.       TPen framePen(TColor::SysWindowFrame);
  703.       dc.SelectObject(framePen);
  704.       TBrush nullBrush(HBRUSH(::GetStockObject(NULL_BRUSH)));
  705.       dc.SelectObject(nullBrush);
  706.       dc.Rectangle(rect);
  707.       rect.Inflate(-1, -1);
  708.       dc.RestoreBrush();
  709.       dc.RestorePen();
  710.     }
  711.   }
  712. }
  713.  
  714. //
  715. // Draw the face of the button [i.e. text and glyph portions]
  716. //
  717. void
  718. TGlyphButton::PaintFace(TDC& dc, TRect& rect)
  719. {
  720.   // Fill the background with the face color
  721.   //
  722.   TBrush brush(TColor::Sys3dFace);
  723.   dc.FillRect(rect, brush);
  724.  
  725.   // Grab the glyph and it's size
  726.   //
  727.   TBitmap* glyph = 0;             // Pointer to glyph
  728.   TRect glyphRect(0, 0, 0, 0);    // Size of glyph
  729.   if (IsSet(biShowGlyph)) {
  730.     // Start with the up bitmap
  731.     //
  732.     glyph = UpBmp;
  733.  
  734.     // Switch to more appropriate bitmap if applicable
  735.     //
  736.     if (IsSet(biPushed) && DownBmp)
  737.       glyph = DownBmp;
  738.     else if (IsSet(biDisabled) && DisabledBmp)
  739.       glyph = DisabledBmp;
  740.  
  741.     CHECK(glyph && glyph->IsGDIObject());
  742.     glyphRect.Set(0, 0, glyph->Width(), glyph->Height());
  743.   }
  744.  
  745.   // Grab some information about the text/caption
  746.   //
  747.   int len = 0;                    // Length of Caption
  748.   TRect textRect(0, 0, 0, 0);     // Size of text
  749.   TAPointer<char> text;           // Pointer to caption dynamic buffer
  750.   TPointer<TFont> tmpFnt;         // Object wrapping font handle
  751.   TColor textColor;               // Color used for button's text
  752.  
  753.   if (IsSet(biShowText)) {
  754.     len = GetWindowTextLength();
  755.     if (len) {
  756.  
  757.       // Select the font
  758.       //
  759.       if (!BtnFont) {
  760.         HFONT hFont = HFONT(::SendMessage(::GetParent(*this), WM_GETFONT,
  761.                                           0, 0));
  762.         if (!hFont)
  763.           hFont = HFONT(GetStockObject(TSystem::Has3dUI() ? ANSI_VAR_FONT :
  764.                                                             SYSTEM_FONT));
  765.         if (hFont)
  766.           tmpFnt = new TFont(hFont);
  767.       }
  768.       if (BtnFont) {
  769.         CHECK(BtnFont->IsGDIObject());
  770.         dc.SelectObject(*BtnFont);
  771.       }
  772.       else if (tmpFnt) {
  773.         CHECK(((TFont&)tmpFnt).IsGDIObject());
  774.         dc.SelectObject((TFont&)tmpFnt);
  775.       }
  776.  
  777.       text = new char[len+1];
  778.       GetWindowText(text, len+1);
  779.       textRect.Set(0, 0, rect.Width() - glyphRect.Width(), SHRT_MAX);
  780.  
  781.       // Display text in proper color
  782.       //
  783.       // Under NT 3.51 and Window 3.1, the system inexplicably returns values
  784.       // for gray text that are the same as either button text (so text never
  785.       // looks grayed) or as button face (so grayed text is invisible).  The
  786.       // extra if statement guards against those problems.
  787.       //
  788.       if (IsSet(biDisabled)) {
  789.         textColor = TColor::SysGrayText;
  790.         if ((textColor == TColor::Sys3dFace.GetValue()) || 
  791.             (textColor == TColor::SysBtnText.GetValue())) {
  792.           textColor = TColor::Sys3dShadow;
  793.         }
  794.       } else {
  795.         textColor = TColor::SysBtnText;
  796.       }
  797.  
  798.       TColor oldColor = dc.SetTextColor(textColor); 
  799.       dc.DrawText(text, len, textRect, DT_WORDBREAK|DT_CALCRECT);
  800.       dc.SetTextColor(oldColor);                    
  801.     }
  802.   }
  803.  
  804.   // If we have text and/or glyph, lay them out and paint them
  805.   //
  806.   if (!textRect.IsNull() || !glyphRect.IsNull()) {
  807.  
  808.     LayoutTextGlyph(rect, textRect, glyphRect);
  809.  
  810.     // Offset things to the lower right if we're in down
  811.     //
  812.     if (IsSet(biPushed)) {
  813.       if (!glyphRect.IsNull() && glyph == UpBmp)
  814.         glyphRect.Offset(1, 1);
  815.       if (!textRect.IsNull())
  816.         textRect.Offset(1, 1);
  817.     }
  818.  
  819.     // Paint the components of the button
  820.     //
  821.     if (!glyphRect.IsNull()) {
  822.       PRECONDITION(glyph && glyph->IsGDIObject());
  823.       TUIFace uiFace(glyphRect, *glyph);
  824.       uiFace.Paint(dc, TPoint(0, 0), TUIFace::Normal, false, true);
  825.     }
  826.     if (!textRect.IsNull()) {
  827.       int mode = dc.SetBkMode(TRANSPARENT);
  828.       TColor oldColor = dc.SetTextColor(textColor);
  829.       dc.DrawText(text, len, textRect, DT_WORDBREAK);
  830.       dc.SetTextColor(oldColor);
  831.       dc.SetBkMode(mode);
  832.     }
  833.   }
  834.  
  835.   // Paint the focus rect [if necessary]
  836.   //
  837.   if (IsSet(biFocus))
  838.     PaintFocusRect(dc, rect);
  839.  
  840.   // Restore font
  841.   //
  842.   if (len && (BtnFont || tmpFnt))
  843.     dc.RestoreFont();
  844. }
  845.  
  846. //
  847. // Virtual routine invoked to retrieve the placement of text and glyph
  848. // when drawing the button.
  849. // Override this routine to customize the layout logic and support
  850. // custom layout styles.
  851. //
  852. void
  853. TGlyphButton::LayoutTextGlyph(const TRect& faceRect, TRect& textRect,
  854.                               TRect& glyphRect)
  855. {
  856.   // Must have either text or a glyph
  857.   //
  858.   PRECONDITION(!textRect.IsNull() || !glyphRect.IsNull());
  859.  
  860.   // First check for the case where we've got either
  861.   // text or glyph - but not both
  862.   //
  863.   if (textRect.IsNull() || glyphRect.IsNull()) {
  864.     TRect& centerRect = textRect.IsNull() ? glyphRect : textRect;
  865.  
  866.     centerRect.Offset(faceRect.left, faceRect.top);
  867.     if (centerRect.Width() < faceRect.Width())
  868.       centerRect.Offset((faceRect.Width() - centerRect.Width())/2, 0);
  869.     else
  870.       centerRect.right = faceRect.right;
  871.     if (centerRect.Height() < faceRect.Height())
  872.       centerRect.Offset(0, (faceRect.Height() - centerRect.Height())/2);
  873.     else
  874.       centerRect.bottom = faceRect.bottom;
  875.   }
  876.  
  877.   // Here we attempt to layout both the glyph and text
  878.   //
  879.   else {
  880.  
  881.     // Align upper left corners of face, text and glyph rectangles
  882.     //
  883.     glyphRect.Offset(faceRect.left, faceRect.top);
  884.     textRect.Offset(faceRect.left, faceRect.top);
  885.  
  886.     // Compute amount of 'extra' space, if any, and how to partition it
  887.     // between the two items
  888.     //
  889.     int space  = faceRect.Width() - glyphRect.Width()- textRect.Width() -
  890.                  LayoutMargin*2;
  891.     int gDelta;
  892.     int tDelta;
  893.  
  894.     switch (LayStyle) {
  895.      case lsH_SGST: {
  896.               gDelta = space >= 0 ? LayoutMargin + space/3 :
  897.                                     LayoutMargin + space/2;
  898.               tDelta = space >= 0 ? gDelta + glyphRect.Width() + space/3 :
  899.                                     gDelta + glyphRect.Width();
  900.             }
  901.             break;
  902.  
  903.       case lsH_GST: {
  904.               gDelta = space >= 0 ? LayoutMargin : LayoutMargin + space/2;
  905.               tDelta = space >= 0 ? gDelta + glyphRect.Width() + space/2 :
  906.                                     gDelta + glyphRect.Width();
  907.             }
  908.             break;
  909.  
  910.  
  911.       default:
  912.         break;
  913.     }
  914.  
  915.     if (LayStyle == lsH_SGST || LayStyle == lsH_GST) {
  916.  
  917.       // Center vertically
  918.       //
  919.       if (textRect.Height() < faceRect.Height())
  920.         textRect.Offset(0, (faceRect.Height() - textRect.Height())/2);
  921.       if (glyphRect.Height() < faceRect.Height())
  922.         glyphRect.Offset(0, (faceRect.Height() - glyphRect.Height())/2);
  923.  
  924.       // Layout horizontally
  925.       //
  926.       glyphRect.Offset(gDelta, 0);
  927.       textRect.Offset(tDelta, 0);
  928.     }
  929.   }
  930. }
  931.  
  932. //
  933. // Specify a 'style' describing how text and glyph should be laid out.
  934. // Invalidate the window if necessary.
  935. //
  936. void
  937. TGlyphButton::SetLayoutStyle(TLayoutStyle style)
  938. {
  939.   if (style != LayStyle) {
  940.     LayStyle = style;
  941.     if (GetHandle())
  942.       Invalidate();
  943.   }
  944. }
  945.  
  946. //
  947. // Sets text coordinates and invalidates window if necessary
  948. //
  949. void
  950. TGlyphButton::SetTextOrigin(int x, int y)
  951. {
  952.   if (x != xText || y != yText) {
  953.     xText = x;
  954.     yText = y;
  955.     if (GetHandle())
  956.       Invalidate();
  957.   }
  958. }
  959.  
  960. //
  961. // Set the upper left corner of glyphs and invalidates window if necessary
  962. //
  963. void
  964. TGlyphButton::SetGlyphOrigin(int x, int y)
  965. {
  966.   if (x != xGlyph || y != yGlyph) {
  967.     xGlyph = x;
  968.     yGlyph = y;
  969.     if (GetHandle())
  970.       Invalidate();
  971.   }
  972. }
  973.  
  974. //
  975. // Display a focus rectangle
  976. //
  977. void
  978. TGlyphButton::PaintFocusRect(TDC& dc, const TRect& faceRect)
  979. {
  980.   PRECONDITION(IsSet(biFocus));
  981.   TRect focusRect = faceRect;
  982.   focusRect.Inflate(FaceToFocusRectDelta, FaceToFocusRectDelta);
  983.   dc.DrawFocusRect(focusRect);
  984. }
  985.  
  986. //
  987. // Repaints window right away by retrieving a client DC and invoking the
  988. // 'Paint' method.
  989. //
  990. void
  991. TGlyphButton::PaintNow()
  992. {
  993.   TRect rect;
  994.   GetClientRect(rect);
  995.   TClientDC dc(*this);
  996.   Paint(dc, false, rect);
  997. }
  998.